package uk.ac.aber.paws.client.power;

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.logging.Level;
import uk.ac.aber.paws.client.core.Initiator;
import uk.ac.aber.paws.client.core.PAWSClient;
import uk.ac.aber.paws.client.db.DBLogger;
import uk.ac.aber.paws.client.util.ComputerRestarter;
import uk.ac.aber.paws.client.util.KickScript;
import uk.ac.aber.paws.client.system.*;
import static uk.ac.aber.paws.client.constants.DatabaseEntryConstants.*;

/**
 * @author Rob Johnson, Aberystwyth University
 * @email rob.johnson@aber.ac.uk
 * @date 16th August 2011
 * 
 * WakeDetector.java
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

public class WakeDetector {

	/**
	 * This records what time (in millis) that the process below successfuly actioned.
	 */
	private static long LAST_HIT = 0l;
	
	/**
	 * This records the time of the last check. It is needed as a milestone as to when the last
	 * check was made to prevent a premature sleep to occur on the computer.
	 */
	private static long LAST_CHECK = 0l;
	
	/**
	 * This indicates in seconds the trigger threshold in which to suspect that the computer has
	 * been to sleep. If the period exceds this value, it is safe to assume that the computer has been asleep.
	 */
	private static long TRIGGER_PERIOD = 0l;
	
	/**
	 * Used to prevent multiple sleep requests being action whilst a wake operation has been detected and processing. 
	 * It takes time to log to the database after just waking up so therefore allows for this to be carried out 
	 * prior to proceeding with any other requests.
	 */
	private static boolean WAKING = false;
	
	/**
	 * This function is repeatedly called to record a trigger time. Once the trigger time is found 
	 * not to have been reset, it assumes that the computer has been asleep and that a sleep period has
	 * just ended.
	 * @param triggerTime This is the time in seconds that is used to determine whether a sleep period has
	 * occurred. Say it is 60 seconds, should a computer go to sleep for longer than this period, then when it wakes up, it
	 * is safe to assume that it has been asleep and logs a wake event to the database. Should you put the computer to
	 * sleep and wake before this 60 second period, it will not detect it as a wake event and therfore will not log to
	 * the database. The only drawback with this is that it will also not prevent another sleep from happening. So a user could
	 * potentially sleep a computer whilst a user is logging in, should they wake the computer within this trigger period. In 
	 * normal circumstances, this will not usually happen.
	 * @param monitorObj
	 */
	public static void register(long triggerTime) {
	
		TRIGGER_PERIOD = triggerTime;
		
		// this is the first time into this procedure so default to current time
		if (LAST_HIT == 0l) {
			LAST_HIT = Calendar.getInstance().getTimeInMillis();
			
			// grant some immunity if the service has just started up...
			PAWSClient.SYSTEM_STATE.getPowerEventInfo().setLastWake(LAST_HIT);
			
		}
		else {
			// get the current time
			long currentTime = Calendar.getInstance().getTimeInMillis();
			
			Initiator.LOG_FILE.log(Level.ALL,"Wake Simulator current Time is: "+new Date(currentTime).toString());
			Initiator.LOG_FILE.log(Level.ALL,"Wake Simulator last wake trigger: "+new Date(LAST_HIT).toString());
			
			// if the current time exceeds the last time it was recorded + the trigger time, then
			// we have just woke up...
			if (currentTime > LAST_HIT + triggerTime * (Integer.parseInt(PAWSClient.getConfig().getProperty("app.main.variables.wakedetectortriggerthreshold"))*1000)) {
			
				Initiator.LOG_FILE.log(Level.CONFIG,"Wake-up Event detected.");
				
				WAKING = true;
				
				// record the new hit time
				LAST_HIT = Calendar.getInstance().getTimeInMillis();
					
				// log this new wake up time to the login session object to be used next time we
				// are in this function
				PAWSClient.SYSTEM_STATE.getPowerEventInfo().setLastWake(currentTime);
				
				(new DBLogger()).registerPowerEvent(PAWSClient.SYSTEM_STATE.getComputerInfo().getComputerName(),WAKEUP_EVENT,new Timestamp(Calendar.getInstance().getTimeInMillis()));
				
				(new NativeCalls()).wakeMonitorUp();
				
				// check for if the restart flag is present following wake event - for use on Library 3M self issue computers
				ComputerRestarter.check();
				
				// check for if a app kick script is in place
				KickScript.check();
				
				// record the new hit time
				LAST_HIT = Calendar.getInstance().getTimeInMillis();
				
				WAKING = false;
			}
			// reset the last hit as the computer is obviously awake and carrying out this process within the 
			// acceptable timescale
			else {
				
				// reset the hit to indicate the process is live and well
				LAST_HIT = currentTime;
				
				// if we have just woken up ie. within the immunity period following the last wake up, then setting -1
				// as the value in the Login object prevents a server oriented sleep from occurring within this period. Say
				// a computer has just woke up, then the client is not allowed to sleep for this period as it will
				// give the user time to login before it automatically sleeps again.
				if (LAST_HIT > (PAWSClient.SYSTEM_STATE.getPowerEventInfo().getLastWake()+(Integer.parseInt(PAWSClient.getConfig().getProperty("app.main.variables.immunityperiodfollowingwake"))*60*1000l))) {
					PAWSClient.SYSTEM_STATE.getPowerEventInfo().setLastWake(-1l);
				}
			}
		}
		
		LAST_CHECK = Calendar.getInstance().getTimeInMillis();
	}
	
	public static boolean pleaseCanITryToSleep() {
		
		if (LAST_HIT == 0l || LAST_CHECK == 0l || TRIGGER_PERIOD == 0l || WAKING) {
			return false;
		}
		
		// using the LAST_CHECK variable, check that this has happened in the last 10 seconds or
		// else the chances are that the computer has just woken and has yet to perform a wake
		// detection, therefore to give the computer a chance to register a wake event, return false
		// and prevent the sleep on this occasion
		if (Calendar.getInstance().getTimeInMillis() > (LAST_CHECK + (TRIGGER_PERIOD*1000l))) {
			return false;
		}
		
		return true;
	}
}
